package com.appshelves.model;

import java.util.List;

import android.content.pm.ResolveInfo;

import com.appshelves.model.Model.Event;
import com.appshelves.utils.ComponentHelper;
import com.appshelves.utils.Profiler;

class ModelImpl extends ModelAPI {

    static ModelImpl _instance;

    private Shelf shelfToChange;
    private List<App> toAdd;
    private List<App> toRemove;

    private boolean deleteShelfRequested;

    /// I/O

    void assureLoaded() {

        getCreator().loadIfNeeded();

        generateLoadEvents();
    }

    private void generateLoadEvents() {
        Profiler p = new Profiler(this, "handling loaded events");

        for (int i = 0; i < getAccessor().getRealShelvesNumber(); i++) {
            Shelf shelf = getAccessor().getShelf(i);
            getEvents().generateEvent(Event.SHELF_LOADED, shelf);
            getEvents().generateEvent(Event.APPS_ADDED_TO_SHELF, shelf, getAccessor().getShelfApps(shelf));
        }

        getEvents().generateEvent(Event.AFTER_LOAD);
        p.e();
    }

    void save() {
        getCreator().save();
    }

    /// 

    void deleteShelf(Shelf shelf) {
        getAccessor().removeAllAppsFromShelf(shelf);
        deleteEmptyShelf(shelf);
    }

    private Shelf addNewShelfToTop() {
        Shelf shelf = getAccessor().addNewShelfToTop();
        if (getAccessor().getRealShelvesNumber() == 1)
            getEvents().generateEvent(Event.THERE_ARE_SHELVES);
        getEvents().generateEvent(Event.SHELF_ADDED_TO_TOP, shelf);
        return shelf;
    }

    private Shelf addNewShelfToBottom() {
        Shelf shelf = getAccessor().addNewShelfToBottom();
        if (getAccessor().getRealShelvesNumber() == 1)
            getEvents().generateEvent(Event.THERE_ARE_SHELVES);
        getEvents().generateEvent(Event.SHELF_ADDED_TO_BOTTOM, shelf);
        return shelf;
    }

    private void deleteEmptyShelf(Shelf shelf) {
        deleteEmptyShelf(shelf, false);
    }

    private void deleteEmptyShelf(Shelf shelf, boolean silent) {
        getAccessor().removeShelf(shelf);

        if (!silent)
            getEvents().generateEvent(Event.SHELF_DELETED, shelf);
        if (!silent && getAccessor().getRealShelvesNumber() == 0)
            getEvents().generateEvent(Event.THERE_ARENT_SHELVES);
    }

    /// change

    public void requestChange(Shelf shelf) {
        shelfToChange = shelf;
    }

    Shelf getShelfToChange() {
        return shelfToChange;
    }

    void requestAddAppsToShelf(List<App> apps) {
        toAdd = apps;
    }

    void requestRemoveAppsFromShelf(List<App> apps) {
        toRemove = apps;
    }

    void requestDeleteShelf() {
        deleteShelfRequested = true;
    }

    boolean performChangeIfNeeded() {
        boolean performed = false;

        if (deleteShelfRequested || toAdd != null || toRemove != null) {
            performChange();
            performed = true;
        }

        shelfToChange = null;
        return performed;
    }

    private void performChange() {
        if (deleteShelfRequested) {
            deleteShelf(shelfToChange);
            deleteShelfRequested = false;
            return;
        }

        if (shelfToChange == null)
            shelfToChange = addNewShelfToTop();

        getAccessor().removeAppsFromShelf(shelfToChange, toRemove);
        getAccessor().addAppsToShelf(shelfToChange, toAdd);

        if (getAccessor().isShelfEmpty(shelfToChange))
            deleteEmptyShelf(shelfToChange);
        else {
            if (toRemove.size() != 0)
                getEvents().generateEvent(Event.APPS_REMOVED_FROM_SHELF, shelfToChange, toRemove);
            if (toAdd.size() != 0)
                getEvents().generateEvent(Event.APPS_ADDED_TO_SHELF, shelfToChange, toAdd);
        }

        toAdd = toRemove = null;
    }

    ///

    void onAppClicked(App app) {
        //        Shelf shelf = getAccessor().getAppShelf(app);
        //        getAccessor().moveShelfToTop(shelf);
        //        getEvents().generateEvent(Event.SHELF_MOVED_TO_TOP, shelf);

        //        shelf.moveAppToTop(app);
        //        getEvents().generateEvent(Event.APP_MOVED_TO_TOP, shelf, app);
    }

    void moveAppToNewShelf(App app, boolean toBottom) {
        Shelf shelf = toBottom ? addNewShelfToBottom() : addNewShelfToTop();
        moveAppToShelf(app, shelf);
    }

    void moveAppToShelf(App app, Shelf shelf) {
        Shelf previousShelf = getAccessor().getAppShelf(app);
        getAccessor().moveAppToAnotherShelf(app, shelf);

        getEvents().generateEvent(Event.APP_REMOVED_FROM_SHELF, previousShelf, app);
        getEvents().generateEvent(Event.APP_MOVED_TO_SHELF, shelf, app);

        if (getAccessor().isShelfEmpty(previousShelf))
            deleteEmptyShelf(previousShelf);
    }

    /// 

    void updateOrAddApp(String packageName) {
        updateOrAddApp(packageName, false);
    }

    void updateOrAddApp(String packageName, boolean silent) {
        App app = getAccessor().updateApp(packageName);
        if (app != null) {
            if (!silent)
                getEvents().generateEvent(Event.APP_UPDATED, app);
            return;
        }
        addNewAppByPackageName(packageName, silent);
    }

    private void addNewAppByPackageName(String packageName, boolean silent) {
        ResolveInfo info = ComponentHelper.resolveInfoByPackageName(packageName);
        if (info == null) // widget or addon package returns null
            return;

        App app = getAccessor().addNewAppToSortedPos(info);

        if (silent)
            return;
        // if (getAccessor().getOffShelfAppsNumber() == 1) // TODO notify there are offshelf apps
        getEvents().generateEvent(Event.OFF_SHELF_APP_ADDED, app);
    }

    void deleteApp(String packageName) {
        deleteApp(packageName, false);
    }

    void deleteApp(String packageName, boolean silent) {
        App app = getAccessor().findAppByPackageName(packageName);
        if (app == null)
            return;

        Shelf shelf = getAccessor().getAppShelf(app);
        getAccessor().deleteApp(shelf, app);

        getEvents().generateEvent(Event.APP_UNINSTALLED, app);

        if (shelf == getAccessor().getOffShelf()) {
            getEvents().generateEvent(Event.OFF_SHELF_APP_DELETED, app);
        } else {
            if (getAccessor().isShelfEmpty(shelf))
                deleteEmptyShelf(shelf, silent);
            else if (!silent)
                getEvents().generateEvent(Event.APP_REMOVED_FROM_SHELF, shelf, app);
        }

    }

    ///

    private ModelImpl() {

    }

    static ModelImpl instance() {
        if (_instance == null)
            _instance = new ModelImpl();
        return _instance;
    }

    ///

    void log() {
        getShelves().log();
    }

}
